#include <event2/event.h>

#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

#define MAX_LINE 16384

void do_read(evutil_socket_t fd, short events, void *arg);
void do_write(evutil_socket_t fd, short events, void *arg);

struct fd_state
{
	char buffer[MAX_LINE];
	size_t buffer_used;

	size_t n_written;
	size_t write_upto;

	struct event *read_event;
	struct event *write_event;
};

struct fd_state * alloc_fd_state(struct event_base *base, evutil_socket_t fd)
{
	struct fd_state *state = (struct fd_state *)malloc(sizeof(struct fd_state));
	if (!state)
	{
		return NULL;
	}

	state->read_event = event_new(base, fd, EV_READ | EV_PERSIST, do_read, state);
	if (!state->read_event)
	{
		free(state);
		return NULL;
	}

	state->write_event = event_new(base, fd, EV_WRITE, do_write, state);
	if (!state->write_event)
	{
		event_free(state->read_event);
		free(state);
		return NULL;
	}

	memset(state->buffer, 0, MAX_LINE);
	state->buffer_used = state->n_written = state->write_upto = 0;

	return state;
}

void free_fd_state(struct fd_state *state)
{
	event_free(state->read_event);
	event_free(state->write_event);
	free(state);
}

void do_read(evutil_socket_t fd, short events, void *arg)
{
	struct fd_state *state = (struct fd_state *) arg;
	char buf[1024];
	int i;
	int result;
	assert(state->write_event);
	while (1)
	{
		memset(buf, 0, 1024);
		result = recv(fd, buf, sizeof(buf), 0);
		if (result <= 0)
		{
			break;
		}
		else
		{
			for (i = 0; i < result; ++i)
			{
				if (state->buffer_used < sizeof(state->buffer))
					state->buffer[state->buffer_used++] = buf[i];
			}
		}
	}
	printf("receive data: %s  size: %d\n", state->buffer + state->n_written, state->write_upto - state->n_written);
	assert(state->write_event);
	event_add(state->write_event, NULL);
	state->write_upto = state->buffer_used;

	if (result == 0)
	{
		printf("connect closed \n");
		free_fd_state(state);
	}
	else if (result < 0)
	{
#ifdef WIN32
		if (result == -1 && WSAGetLastError() == WSAEWOULDBLOCK)
			return;
#else
		if (errno == EAGAIN)
			return;
#endif
		perror("recv");
		free_fd_state(state);
	}
}

void do_write(evutil_socket_t fd, short events, void *arg)
{
	struct fd_state *state = (struct fd_state *)arg;

	while (state->n_written < state->write_upto)
	{
		int result = send(fd, state->buffer + state->n_written,
			state->write_upto - state->n_written, 0);
		if (result < 0)
		{
#ifdef WIN32
			if (result == -1 && WSAGetLastError() == WSAEWOULDBLOCK)
				return;
#else
			if (errno == EAGAIN)
				return;
#endif
			free_fd_state(state);
			return;
		}
		assert(result != 0);
		printf("send data: %s \n", state->buffer + state->n_written);

		state->n_written += result;
	}

	//buffer is full
	if (state->n_written == state->buffer_used)
	{
		state->n_written = state->write_upto = state->buffer_used = 0;
		memset(state->buffer, 0, MAX_LINE);
	}
}

void do_accept(evutil_socket_t listener, short event, void *arg)
{
	struct event_base *base = (struct event_base *)arg;
	struct sockaddr_in ss;
	int slen = sizeof(ss);
	int fd = accept(listener, (struct sockaddr*)&ss, &slen);
	if (fd > 0)
	{
		printf("accept socket %d, address %s \n", fd, inet_ntoa(ss.sin_addr));
		struct fd_state *state;
		evutil_make_socket_nonblocking(fd);
		state = alloc_fd_state(base, fd);
		assert(state);
		assert(state->read_event);
		event_add(state->read_event, NULL);
	}
}

void run()
{
	int listener;
	struct sockaddr_in addr_server;
	struct event_base *base;
	struct event *listener_event;

	base = event_base_new();
	if (!base)
	{
		perror("event_base_new error");
		return;
	}

	addr_server.sin_addr.S_un.S_addr = ADDR_ANY;
	addr_server.sin_family = AF_INET;
	addr_server.sin_addr.s_addr = 0;
	addr_server.sin_port = htons(10286);

	listener = socket(AF_INET, SOCK_STREAM, 0);
	evutil_make_socket_nonblocking(listener);

	int one = 1;
	setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, sizeof(one));

	if (bind(listener, (struct sockaddr*)&addr_server, sizeof(addr_server)) < 0)
	{
		perror("bind error");
		return;
	}

	if (listen(listener, 10)<0)
	{
		perror("listen error");
		return;
	}

	printf("server is listening ... \n");

	listener_event = event_new(base, listener, EV_READ | EV_PERSIST, do_accept, (void*)base);
	event_add(listener_event, NULL);
	event_base_dispatch(base);
}

int init_win_socket()
{
	WSADATA wsaData;

	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		return -1;
	}

	return 0;
}

int main(int c, char **v)
{

#ifdef WIN32
	init_win_socket();
#endif

	run();

	getchar();
	return 0;
}